﻿using BookEFCore;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using WebApplication1.CacheHelper;
using ZiggyCreatures.Caching.Fusion;

namespace WebApplication1.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class TestController : ControllerBase
    {
        private readonly MyDbContext _myDbContext;
        private readonly IMemoryCache _memoryCache;
        private readonly ILogger<TestController> _logger;
        private readonly IDistributedCache _distributedCache;
        private readonly IDistributedCacheHelper _distributedCacheHelper;
        private readonly IFusionCache _fusionCache;

        public TestController(MyDbContext myDbContext, IMemoryCache memoryCache, ILogger<TestController> logger, IDistributedCache distributedCache, IDistributedCacheHelper distributedCacheHelper, IFusionCache fusionCache)
        {
            _myDbContext = myDbContext;
            _memoryCache = memoryCache;
            _logger = logger;
            _distributedCache = distributedCache;
            _distributedCacheHelper = distributedCacheHelper;
            _fusionCache = fusionCache;
        }

        [HttpGet]
        public async Task<IActionResult> Get()
        {
            _myDbContext.Books.Add(new Book
            {
                Id = Guid.NewGuid(),
                Name = "零基础学C语言",
                Price = 59
            });
            await _myDbContext.SaveChangesAsync();

            var books = await _myDbContext.Books.FirstAsync();
            return Ok(books);
        }

        [HttpGet("now")]
        [ResponseCache(Duration = 60)]
        public DateTime Now()
        {
            return DateTime.Now;
        }

        [HttpGet("cacheBooks")]
        public async Task<Book[]> GetBooks()
        {
            _logger.LogInformation("开始执行 GetBooks");
            var items = await _memoryCache.GetOrCreateAsync("AllBooks", async entry =>
            {
                _logger.LogInformation("缓存中没有数据，开始从数据库中获取数据");
                entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5);
                entry.SlidingExpiration = TimeSpan.FromMinutes(1);
                entry.Priority = CacheItemPriority.Normal;
                var books = await _myDbContext.Books.ToArrayAsync();
                return books;
            });
            //var books = await _myDbContext.Books.ToArrayAsync();
            _logger.LogInformation("GetBooks 执行完成");
            return items;
        }

        /// <summary>
        /// 这个方法会有缓存穿透的问题，也就是如果一个id在数据库中不存在，则会一直执行数据库查询操作，也会影响数据库的性能
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("cacheBook/{id}")]
        public async Task<ActionResult<Book?>> GetBook(Guid id)
        {
            string cacheKey = $"Book-{id}";
            /* Book? book = _memoryCache.Get<Book?>(cacheKey);
             if(book != null)
             {
                 _logger.LogInformation("从缓存中获取数据");
                 return book;
             }
             else
             {
                 book = await _myDbContext.Books.FindAsync(id);
                 if (book == null)
                 {
                     return NotFound();
                 }
                 else
                 {
                     _memoryCache.Set(cacheKey, book, TimeSpan.FromMinutes(5));
                 }
             }*/

            // 使用 GetOrCreateAsync 方法来获取缓存可用避免缓存穿透的问题
            _logger.LogInformation("开始执行 GetBook");
            var book = await _memoryCache.GetOrCreateAsync(cacheKey, async (e) =>
            {
                var b = await _myDbContext.Books.FindAsync(id);
                _logger.LogInformation("数据库查询:{0}", b == null ? "为空" : "不为空");
                return b;
            });
            _logger.LogInformation("执行结束：{0}", book == null ? "为空" : "不为空");
            return book;
        }

        [HttpGet("cacheBookFromDistributedCache/{id}")]
        public async Task<IActionResult> GetBookFromDistributedCache(Guid id)
        {
            string cacheKey = $"Book-{id}";
            var book = await _distributedCache.GetStringAsync(cacheKey);
            if (book != null)
            {
                return Ok(book);
            }
            else
            {
                var b = await _myDbContext.Books.FindAsync(id);
                if (b == null)
                {
                    return NotFound();
                }
                else
                {
                    await _distributedCache.SetStringAsync(cacheKey, b.ToString(), new DistributedCacheEntryOptions
                    {
                        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5),
                        SlidingExpiration = TimeSpan.FromMinutes(1)
                    });
                    return Ok(b);
                }
            }
        }

        [HttpGet("distributedCachedHelper/{id}")]
        public async Task<IActionResult> DistributedCachedHelper(Guid id)
        {
            string cacheKey = $"Book-{id}";
            var book = await _distributedCacheHelper.GetOrCreateAsync(cacheKey, async (e) =>
            {
                var b = await _myDbContext.Books.FindAsync(id);
                _logger.LogInformation("数据库查询:{0}", b == null ? "为空" : "不为空");
                return b;
            });
            return book == null ? NotFound() : Ok(book);
        }

        /// <summary>
        /// 也解决了缓存穿透问题
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet("fusionCache/{id}")]
        public async Task<IActionResult> TestFusionCache(Guid id)
        {
            var book = await _fusionCache.GetOrSetAsync<Book?>($"Book:{id}", async entry =>
            {
                var b = await _myDbContext.Books.FindAsync(id);
                _logger.LogInformation("数据库查询:{0}", b == null ? "为空" : "不为空");
                return b;
            });
            return book == null ? NotFound() : Ok(book);
        }

        /// <summary>
        /// 测试异常
        /// </summary>
        /// <returns></returns>
        [HttpGet("ThrowException")]
        public IActionResult ThrowException()
        {
            // 如果这里没有 try-catch，异常会被全局异常处理器捕获
            try
            {
                throw new Exception("测试异常");
            }
            catch (Exception ex)
            {
                return Ok($"发生了异常：{ex.Message}");
            }

        }

        /// <summary>
        /// 测试异常过滤器
        /// </summary>
        /// <returns></returns>
        [HttpGet("TestActionFilter")]
        public IActionResult TestActionFilter()
        {
            Console.WriteLine("执行 TestActionFilter");
            return Ok("执行 TestActionFilter");
        }

        /// <summary>
        /// 测试事务过滤器
        /// </summary>
        /// <returns></returns>
        [HttpGet("TestTransactionFilter")]
        public async Task<IActionResult> TestTransactionFilter()
        {
            _myDbContext.Books.Add(new Book
            {
                Id = Guid.NewGuid(),
                Name = "零基础学C语言2",
                Price = 69
            });
            await _myDbContext.SaveChangesAsync();

            // 抛出异常后上一个保存的数据也会被回滚
            throw new Exception("测试异常");

            _myDbContext.Books.Add(new Book
            {
                Id = Guid.NewGuid(),
                Name = "零基础学C语言3",
                Price = 79
            });
            await _myDbContext.SaveChangesAsync();
            return Ok("保存成功");
        }
    }
}
